﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CML.Services.Interfaces;
using System.Xml;
using CML.Data;
using CML.Data.Instructions;

namespace CML.Services.Implementations
{
	public class InstructionService : IInstructionService
	{
		private readonly IXmlService XmlService;

		public InstructionService(IXmlService xmlService)
		{
			XmlService = xmlService;
		}

		public void Add(XmlDocument doc, AddXmlInstruction i)
		{
			var n = doc.CreateNavigator();
			n.MoveToRoot();
			var nsm = XmlService.GetNamespaceManager(n);
			n = n.SelectSingleNode(i.xpath, nsm);
			if (n == null)
				throw new Exception("Node not found for xpath");
			switch (i.location)
			{
				case AddXmlInstruction.Locations.before:
					n.InsertBefore(i.xml);
					break;
				case AddXmlInstruction.Locations.after:
					n.InsertAfter(i.xml);
					break;
				default: //to
					n.AppendChild(i.xml);
					break;
			}
		}

		public void Update(XmlDocument doc, UpdateXmlInstruction i)
		{
			var n = doc.CreateNavigator();
			var nsm = XmlService.GetNamespaceManager(n);
			n.MoveToRoot();
			n = n.SelectSingleNode(i.xpath, nsm);
			if (n == null)
				throw new Exception("Node not found for xpath");
			n.SetValue(i.value);
		}

		public void Delete(XmlDocument doc, DeleteXmlInstruction i)
		{
			var n = doc.CreateNavigator();
			var nsm = XmlService.GetNamespaceManager(n);
			n.MoveToRoot();
			n = n.SelectSingleNode(i.xpath, nsm);
			if (n == null)
				throw new Exception("Node not found for xpath");
			n.DeleteSelf();
		}

		public void ApplyXmlInstruction(XmlDocument doc, Instruction i)
		{
			if (i is AddXmlInstruction)
				Add(doc, (AddXmlInstruction)i);
			if (i is DeleteXmlInstruction)
				Delete(doc, (DeleteXmlInstruction)i);
			if (i is UpdateXmlInstruction)
				Update(doc, (UpdateXmlInstruction)i);
		}

		public void ApplyPropertyInstruction(IList<Property> pl, Instruction i)
		{
			if (i is AddPropertyInstruction)
				Add(pl, (AddPropertyInstruction)i);
			if (i is DeletePropertyInstruction)
				Delete(pl, (DeletePropertyInstruction)i);
			if (i is UpdatePropertyInstruction)
				Update(pl, (UpdatePropertyInstruction)i);

		}

		public void Add(IList<Property> pl, AddPropertyInstruction i)
		{
			pl.Add(new Property() { 
				key = i.key,
				value = i.value
			});
		}

		public void Update(IList<Property> pl, UpdatePropertyInstruction i)
		{
			var p = pl.FirstOrDefault(x => x.key == i.key);
			if (p != null)
				p.value = i.value;
		}

		public void Delete(IList<Property> pl, DeletePropertyInstruction i)
		{
			var p = pl.FirstOrDefault(x => x.key == i.key);
			if (p != null)
				pl.Remove(p);
		}
	}
}
